

import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import java.io.*;
import java.util.Random;
import javax.microedition.media.*;

/**
 * @author byoms23
 */
public class FingersDanceLogic extends GameCanvas 
        implements Runnable {
    public static final int
            LEVEL1 = 0,
            LEVEL2 = 1,
            LEVEL3 = 2;

    // Declare variables and objets of instance
    private boolean ended, direction;
    private int frameTime, mapy;
    private long score, time, velocity, hits, miss;
    private LayerManager lm;
    private TiledLayer tiles;
    private Arrow[] sequence;
    private int inicio, level;
    private Player sound;
    private Display display;

    /**
     * constructor
     */
    public FingersDanceLogic(Display display) {
        // Configure father's constructor
        super(true);

        // Configure variables and objects of instance
        this.display = display;
        ended = true;
        frameTime = 100;
        mapy = 0;
        score = -1;
        tiles = background();
        lm = new LayerManager();
        lm.append(tiles);
        
        inicio = -1;
        level = -1;
        time = -1;
        hits = -1;
        velocity = -1;
        sequence = null;
    }

    /**
     *
     */
    private TiledLayer background() {
        Image imagen = null;
        TiledLayer tl = null;
        String path = "/mosaico3.png";

        try {
            imagen = Image.createImage(path);
        } catch (IOException ioe) {
            System.out.print("\n\nERROR: \'" + path + "\' does not exist.\n\n");
        }

        tl = new TiledLayer(10, 50, imagen, 32, 32);
        Random randomizer = new Random();
        for (int i = 0;  i < tl.getColumns(); i++) {
            for(int j = 0; j < tl.getRows(); j++) {
                tl.setCell(i, j, randomizer.nextInt(
                        (imagen.getHeight() / tl.getCellHeight()) *
                        (imagen.getWidth() / tl.getCellWidth())) + 1);
            }
        }

        return tl;
    }

    /**
     *
     */
    private Arrow[] getEasyLevel() {
        // Declare variables and objects
        Arrow[] result = null;
        Random random = new Random();

        // Create sequences
        try{
            Arrow[] sequence1 = {
                    new Arrow(Arrow.UP, 7000),
                    new Arrow(Arrow.LEFT, 8000),
                    new Arrow(Arrow.DOWN, 9000),
                    new Arrow(Arrow.DOWN, 10000),
                    new Arrow(Arrow.RIGHT, 11000),
                    new Arrow(Arrow.UP, 12000),
                    new Arrow(Arrow.RIGHT, 13000),
                    new Arrow(Arrow.LEFT, 14000),
                    new Arrow(Arrow.UP, 15000),
                    new Arrow(Arrow.DOWN, 15700),
                    new Arrow(Arrow.LEFT, 16500),
                    new Arrow(Arrow.DOWN, 17600),
                    new Arrow(Arrow.UP, 18100),
                    new Arrow(Arrow.RIGHT, 18900),
                    new Arrow(Arrow.DOWN, 19500),
                    new Arrow(Arrow.LEFT, 21100),
                    new Arrow(Arrow.UP, 21900),
                    new Arrow(Arrow.RIGHT, 22700),
                    new Arrow(Arrow.DOWN, 23500),
                    new Arrow(Arrow.UP, 24300),
                    new Arrow(Arrow.RIGHT, 25100),
                    new Arrow(Arrow.LEFT, 26000),
                    new Arrow(Arrow.DOWN, 26800),
                    new Arrow(Arrow.UP, 27700),
                    new Arrow(Arrow.UP, 28600),
                    new Arrow(Arrow.LEFT, 29500),
                    new Arrow(Arrow.RIGHT, 30800),
                    new Arrow(Arrow.LEFT, 32000),
                    new Arrow(Arrow.UP, 33000),
                    new Arrow(Arrow.DOWN, 34000),
                    new Arrow(Arrow.LEFT, 35000),
                    new Arrow(Arrow.RIGHT, 36000),
                    new Arrow(Arrow.DOWN, 36700),
                    new Arrow(Arrow.UP, 371400),
                    new Arrow(Arrow.RIGHT, 38100)
                },
                sequence2 = {
                    new Arrow(Arrow.DOWN, 7000),
                    new Arrow(Arrow.LEFT, 75000),
                    new Arrow(Arrow.UP, 9000),
                    new Arrow(Arrow.LEFT, 10000),
                    new Arrow(Arrow.DOWN, 11000),
                    new Arrow(Arrow.RIGHT, 12000),
                    new Arrow(Arrow.LEFT, 13000),
                    new Arrow(Arrow.DOWN, 14000),
                    new Arrow(Arrow.UP, 15000),
                    new Arrow(Arrow.LEFT, 15700),
                    new Arrow(Arrow.LEFT, 16500),
                    new Arrow(Arrow.UP, 17600),
                    new Arrow(Arrow.RIGHT, 18100),
                    new Arrow(Arrow.LEFT, 18900),
                    new Arrow(Arrow.UP, 19500),
                    new Arrow(Arrow.DOWN, 21100),
                    new Arrow(Arrow.LEFT, 21900),
                    new Arrow(Arrow.RIGHT, 22700),
                    new Arrow(Arrow.LEFT, 23500),
                    new Arrow(Arrow.LEFT, 24300),
                    new Arrow(Arrow.RIGHT, 25100),
                    new Arrow(Arrow.DOWN, 26000),
                    new Arrow(Arrow.RIGHT, 26800),
                    new Arrow(Arrow.UP, 27700),
                    new Arrow(Arrow.UP, 28600),
                    new Arrow(Arrow.DOWN, 29500),
                    new Arrow(Arrow.LEFT, 30800),
                    new Arrow(Arrow.RIGHT, 32000),
                    new Arrow(Arrow.LEFT, 33000),
                    new Arrow(Arrow.UP, 34000),
                    new Arrow(Arrow.RIGHT, 35000),
                    new Arrow(Arrow.LEFT, 36000),
                    new Arrow(Arrow.UP, 36700),
                    new Arrow(Arrow.DOWN, 371400),
                    new Arrow(Arrow.LEFT, 38100)
                };

            // Select a sequence
            if(random.nextInt(2) == 0) {
                result = sequence1;
            } else {
                result = sequence2;
            }
        } catch (Exception ex) {
            // Mostrar error:
            
        }

        // Return the sequence
        return result;
    }

    /**
     *
     */
    public void start(int level) {
        // Declare variables and objects
        String path = null;

        // Determine the start of a game
        ended = false;
        score = 0;
        hits = 0;
        miss = 0;

        direction = false;
        inicio = 0;
        this.level = level;
        if(this.level == LEVEL1) {
            sequence = getEasyLevel();
            path = "/classical.wav";
            velocity = 8;
        } else if(this.level == LEVEL2) {
        } else if(this.level == LEVEL3) {
        }

        try {
            InputStream in =
            getClass().getResourceAsStream(path);
            sound = Manager.createPlayer(in, "audio/x-wav");
            time = System.currentTimeMillis();
            sound.start();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (MediaException ex) {
            ex.printStackTrace();
        } catch (Exception ex) {
            System.err.println("ERROR; " + ex.getMessage());
        }

        new Thread(this).start();
    }

    /**
     *
     */
    public void stop() {
        try {
        sound.stop();
        } catch (Exception ex) {

        }
    }
    
    /**
     * Called when the game is run.
     */
    public void run() {
        // Declare variables and objets
        long start, end;
        int duration;
        Graphics g = getGraphics();

        // Game cicle
        while (!ended) {
            start = System.currentTimeMillis();
            if(!(sound.getState() == Player.STARTED)) {
                ended = true;
            } else {
                repaint(g);
                
                // Synchronization
                end = System.currentTimeMillis();
                duration = (int)(end - start);
                if (duration < frameTime) {
                    try {
                        Thread.sleep(frameTime - duration);
                    } catch (InterruptedException ie) {
                        ended = true;
                    }
                }
            }
        }

        // Show the final message to the user
        Alert message = new Alert("GAME OVER",
                "¡¡¡ GAME OVER !!!\n\n" +
                "Your score is: " + score + ".\n" +
                "Your performance are: " + ((sequence.length - miss) * 100 / sequence.length) + "%.\n" +
                "Your misses are: " + miss + ".\n"
                , null, AlertType.CONFIRMATION);
        message.setTimeout(Alert.FOREVER);
        display.setCurrent(message);

        
    }

    /**
     *
     */
    private void repaint(Graphics g) {
        // Change the background
        int change = 0;
        if(mapy + tiles.getRows() * tiles.getCellHeight() <= getHeight()) {
            direction = true;
        } else if(mapy > 0) {
            direction = false;
        }

        // Take the shift the background
        if (direction) {
            change = 2;
        } else {
            change = -2;
        }
        mapy += change;

        // Clean screan
        g.setColor(0xffffff);
        g.fillRect(0, 0, getWidth(), getHeight());

        // draw the background
        tiles.setPosition(0, mapy);
        lm.paint(g, 0, 0);

        // Draw the arrows
        int keyStates = getKeyStates();
        long current = System.currentTimeMillis() - time;
        boolean key = false, exit = false;
        for(int i = inicio; (i < sequence.length) ; i++) {
            int posy = (int)(getHeight() - (getHeight() - velocity * (sequence[i].getTime() - current) / frameTime));
            if((posy <= 1) && (sequence[i].getFrame() == 0)) {
                    sequence[i].setFrame(2);
                    hits = 0;
                    miss++;
                    key = false;
            } else
            if((sequence[i].getTime() - ((getHeight() * frameTime) / velocity) < current)
                    && !(current > sequence[i].getTime())) {
                // Draw the arrow
                LayerManager lmTemp = new LayerManager();

                // Locate the position and the actual state of the arrow
                if((sequence[i].getFrame() == 1) || (sequence[i].getFrame() == 2)) {
                    key = false;
                } else if((posy + sequence[i].getHeight() > 33) && (posy <= 64)) {
                    key = true;
                } else {
                    key = false;
                }

                // Determine keys pressed
                if(!exit) {
                    if(((keyStates & LEFT_PRESSED) != 0) && (key)) {
                        if(sequence[i].getType().equals(Arrow.LEFT)){
                            score += ++hits * 10;
                            // Change the arrow
                            sequence[i].setFrame(1);
                            exit = true;
                        } else {
                            sequence[i].setFrame(2);
                            hits = 0;
                            miss++;
                        }
                    }
                    if(((keyStates & RIGHT_PRESSED) != 0) && (key)) {
                        if(sequence[i].getType().equals(Arrow.RIGHT)){
                            score += ++hits * 10;
                            // Change the arrow
                            sequence[i].setFrame(1);
                            exit = true;
                        } else {
                            sequence[i].setFrame(2);
                            hits = 0;
                            miss++;
                        }
                    }
                    if(((keyStates & UP_PRESSED) != 0) && (key)) {
                        if(sequence[i].getType().equals(Arrow.UP)){
                            score += ++hits * 10;
                            // Change the arrow
                            sequence[i].setFrame(1);
                            exit = true;
                        } else {
                            sequence[i].setFrame(2);
                            hits = 0;
                            miss++;
                        }
                    }
                    if(((keyStates & DOWN_PRESSED) != 0) && (key)) {
                        if(sequence[i].getType().equals(Arrow.DOWN)){
                            score += ++hits * 10;
                            // Change the arrow
                            sequence[i].setFrame(1);
                            exit = true;
                        } else {
                            sequence[i].setFrame(2);
                            hits = 0;
                            miss++;
                        }
                    }
                }
                
                // Configure arrow
                sequence[i].setPosition(
                       - (sequence[i].getWidth() / 2) +
                        ((sequence[i].getType().equals(Arrow.LEFT))? (1) * (getWidth() / 8) :
                            (sequence[i].getType().equals(Arrow.UP))? (3) * (getWidth() / 8):
                                (sequence[i].getType().equals(Arrow.RIGHT))? (5) * (getWidth() / 8) :
                                    (sequence[i].getType().equals(Arrow.DOWN))? (7) * (getWidth() / 8) :
                                    0), posy);
                //(int)(getHeight() - (getHeight() - velocity * (sequence[i].getTime() - current) / frameTime)));
                lmTemp.append(sequence[i]);
                lmTemp.paint(g, 0, 0);
            }
        }

        // Draw the text of the score
        // draw a fill rect
        g.setColor(0xffffff);
        g.fillRect(0, 0, getWidth(), 32);
        g.setColor(0x0B0B3B);
        g.drawString("Score: " + score, getWidth() / 2, 10, Graphics.TOP | Graphics.HCENTER);
        g.drawString("Hits: " + hits, getWidth(), 11, Graphics.TOP | Graphics.RIGHT);
        g.drawString("Missed: " + miss, 0, 11, Graphics.TOP | Graphics.LEFT);

        // To the rectangles for the arrows
        g.setColor(0xBE81F7);
        g.setStrokeStyle(Graphics.DOTTED);
        g.drawRect(0, 32 * 2, getWidth() / 4, getHeight() - 32 * 2);
        g.drawRect(getWidth() / 4, 32 * 2, getWidth() / 4, getHeight() - 32 * 2);
        g.drawRect(getWidth() / 2, 32 * 2, getWidth() / 4, getHeight() - 32 * 2);
        g.drawRect(3 * getWidth() / 4, 32 * 2, getWidth() / 4, getHeight() - 32 * 2);
        // To the main place
        g.setColor(0xffff00);
        g.setStrokeStyle(Graphics.SOLID);
        g.drawRect(0, 32,getWidth(), 32);

        // Graph the screen
        flushGraphics();
    }
}